// Package service
// @Author: zhangdi
// @File: ListenMouse
// @Version: 1.0.0
// @Date: 2023/5/23 13:38
package service

import (
	"fmt"
	"github.com/moutend/go-hook/pkg/mouse"
	"github.com/moutend/go-hook/pkg/types"
	"ideatools/application/constant"
	"ideatools/application/pkgs/utils"
	"os"
	"os/signal"
	"time"
)

const (
	DoubleClickDuration = 500 // 双击时间间隔为500ms （同一按键间隔小于该值则认为是双击）
	//	LongPressDuration   = 550 // 长按时间阈值
)

var (
	mouseButtonDownLists = []types.Message{
		constant.MouseLeftButtonPressed,
		constant.MouseMiddleButtonPressed,
		constant.MouseRightButtonPressed,
		constant.MouseSideButtonPressed,
	}
	mouseButtonUpLists = []types.Message{
		constant.MouseLeftButtonReleased,
		constant.MouseMiddleButtonReleased,
		constant.MouseRightButtonReleased,
		constant.MouseSideButtonReleased,
	}
)

// MouseEventHandler 鼠标事件回调函数
type MouseEventHandler func(eventName constant.MouseEvent, MouseButton string)

type MouseListenService struct {
	//当前按下的鼠标按键列表
	CurrDownKeyList []types.MouseEvent
	LastDownKey     types.MouseEvent //上一个按键（最后被PressList移除的按键）
	EventFunc       MouseEventHandler
}

// StartMouseListenService 监听鼠标
func StartMouseListenService(eventFunc MouseEventHandler) *MouseListenService {
	s := &MouseListenService{
		CurrDownKeyList: make([]types.MouseEvent, 0),
		EventFunc:       eventFunc,
	}
	go s.ListenMouse()
	return s
}

func (s *MouseListenService) ListenMouse() error {
	// Buffer size is depends on your need. The 100 is placeholder value.
	mouseChan := make(chan types.MouseEvent, 100)

	if err := mouse.Install(nil, mouseChan); err != nil {
		return err
	}

	defer mouse.Uninstall()

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)

	fmt.Println("start capturing mouse input")

	for {
		select {
		//case <-time.After(5 * time.Minute):
		//	fmt.Println("Received timeout signal")
		//	return nil
		case <-signalChan:
			fmt.Println("Received shutdown signal")
			return nil
		case e := <-mouseChan:
			if e.Message == constant.MouseMove { //鼠标移动，则不做任何处理
				continue
			}
			//鼠标某个按键按下
			if utils.ArrContains(mouseButtonDownLists, e.Message) {
				//fmt.Printf("新增按键%v \n", e.Message)
				s.CurrDownKeyList = append(s.CurrDownKeyList, e)
				// 睡眠指定时间后在进行长按判断
				go func() {
					time.Sleep(time.Millisecond * time.Duration(LongPressDuration))
					if s.KeyIsInDownList(e.Message) {
						s.EventFunc(constant.MouseEventLongClick, s.GetButtonName(e.Message)) //调用事件通知：长按事件
						//fmt.Println("长按事件", e.Message, len(s.CurrDownKeyList), s.CurrDownKeyList[0].Message)
					}
				}()
			}

			//鼠标某个按键抬起
			if utils.ArrContains(mouseButtonUpLists, e.Message) {

				//判断是否为双击事件--注意顺序不能乱，需要在删除（DelItemPressList）之前
				if s.LastDownKey.Message == (e.Message - 1) { //是否双击
					if (e.Time - s.LastDownKey.Time) < DoubleClickDuration {
						s.EventFunc(constant.MouseEventDoubleClick, s.GetButtonName(e.Message)) //调用事件通知：双击事件
						//fmt.Println("双击事件")
					} else { //单击事件
						s.EventFunc(constant.MouseEventOneClick, s.GetButtonName(e.Message)) //调用事件通知：双击事件
						//fmt.Println("单击事件")
					}
				}
				s.DelItemPressList(e) //删除松开事件

				//将按键中的按键从按键列表s.PressList删除
				//if !s.KeyIsInDownList(e.Message - 1) {
				//	fmt.Printf("移除按键%v \n", e.Message)
				//}

			}
			continue
		}
	}

	// not reached
	return nil
}

func (s *MouseListenService) DelItemPressList(DelEvent types.MouseEvent) {
	newPressList := make([]types.MouseEvent, 0)
	for _, event := range s.CurrDownKeyList {
		// DelEvent.Message-1 就意味着把对应按键的按下建移除
		if event.Message != DelEvent.Message-1 {
			newPressList = append(newPressList, event)
		} else {
			s.LastDownKey = event
		}

	}
	s.CurrDownKeyList = newPressList
}

func (s *MouseListenService) KeyIsInDownList(code types.Message) bool {
	for _, event := range s.CurrDownKeyList {
		if event.Message == code {
			return true
		}
	}

	return false
}

// GetButtonName 根据按键码获取按键名
func (s *MouseListenService) GetButtonName(message types.Message) string {
	switch message {
	case constant.MouseLeftButtonPressed:
		return constant.MouseButtonLeft
	case constant.MouseLeftButtonReleased:
		return constant.MouseButtonLeft
	case constant.MouseMiddleButtonPressed:
		return constant.MouseButtonMiddle
	case constant.MouseMiddleButtonReleased:
		return constant.MouseButtonMiddle
	case constant.MouseRightButtonPressed:
		return constant.MouseButtonRight
	case constant.MouseRightButtonReleased:
		return constant.MouseButtonRight
	case constant.MouseSideButtonPressed:
		return constant.MouseButtonSide
	case constant.MouseSideButtonReleased:
		return constant.MouseButtonSide
	}
	return ""
}
